package easier.fast.service;

import android.annotation.SuppressLint;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;
import com.ejlchina.okhttps.Download.Ctrl;
import com.ejlchina.okhttps.Download.Failure;
import com.ejlchina.okhttps.HTTP;
import com.ejlchina.okhttps.HTTP.OkConfig;
import com.ejlchina.okhttps.HttpResult;
import com.ejlchina.okhttps.OkHttps;
import com.ejlchina.okhttps.Process;
import com.ejlchina.okhttps.SHttpTask;
import com.google.gson.Gson;
import easier.app.x.CommonCode;
import easier.fast.service.data.CodeDataMsgException;
import easier.fast.service.data.CodeMsgData;
import easier.fast.service.data.Data;
import easier.log.logger.Logger;
import easier.safe.gson.GsonFactory;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.annotations.NonNull;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.core.ObservableEmitter;
import io.reactivex.rxjava3.core.ObservableOnSubscribe;
import io.reactivex.rxjava3.core.ObservableSource;
import io.reactivex.rxjava3.functions.Function;
import io.reactivex.rxjava3.schedulers.Schedulers;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.Proxy;
import java.util.Calendar;
import java.util.List;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import okhttp3.OkHttpClient.Builder;

public class ApiService {

    private static final String TAG = "ApiService";
    /**
     * post请求需要添加的
     */
    private static String sUserAgent;

    /**
     * 标记是否开启log输出
     */
    private static final AtomicBoolean LOGGABLE = new AtomicBoolean( false );

    /**
     * 网络请求
     */
    private static final HTTP HTTP = OkHttps.newBuilder().config( new OkConfig() {
        @Override
        public void config( Builder builder ) {

            builder.proxy( Proxy.NO_PROXY );
            builder.connectTimeout( 30, TimeUnit.SECONDS );
            builder.readTimeout( 30, TimeUnit.SECONDS );
            builder.writeTimeout( 30, TimeUnit.SECONDS );
        }
    } ).build();

    /**
     * 获取时间
     */
    private static final Calendar calendar = Calendar.getInstance( TimeZone.getTimeZone( "GMT+:08:00" ) );

    /**
     * @param loggable 设置是否可以打印log
     */
    public static void setLoggable( boolean loggable ) {

        LOGGABLE.set( loggable );
    }

    /**
     * @param context get请求
     * @param url     地址
     *
     * @return 结果
     */
    public static Observable< String > get( Context context, @NonNull String url ) {

        return Observable.just( url )
            .flatMap( new Function< String, ObservableSource< String > >() {
                @Override
                public ObservableSource< String > apply( String s ) throws Throwable {

                    HttpResult httpResult = HTTP.sync( s ).get();
                    switch ( httpResult.getState() ) {
                        case RESPONSED:
                            String result = httpResult.getBody().toString();
                            return Observable.just( result );
                        case NETWORK_ERROR:
                        case TIMEOUT:
                        case EXCEPTION:
                        case CANCELED:
                        default:
                            return Observable.error( httpResult.getError() );
                    }
                }
            } )
            .subscribeOn( Schedulers.io() )
            .observeOn( AndroidSchedulers.mainThread() );
    }

    /**
     * @return sign
     */
    public static String createSign( String deviceId ) {

        String sign =
            AESUtil.getAppKey() + "@" + calendar.getTimeInMillis() + "@" + CommonCode.randomString( 6 ) + "@" + deviceId;

        try {
            return AESUtil.encrypt( sign );
        } catch ( Exception e ) {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * @param context 创建post请求
     * @param data    请求参数数据
     *
     * @return 请求结果
     */
    public static Observable< CodeMsgData > post( Context context, Data data ) {

        return Observable.just( data )
            .flatMap( new Function< Data, Observable< CodeMsgData > >() {
                @Override
                public Observable< CodeMsgData > apply( Data data ) throws Throwable {

                    String json = data.toJson();
                    SHttpTask httpTask = HTTP.sync( data.api )
                        .addHeader( "User-Agent", getUserAgent( context ) )
                        .addHeader( "Connection", "Keep-Alive" )
                        .addBodyPara( "data", AESUtil.encrypt( json ) );

                    if ( LOGGABLE.get() ) {
                        synchronized (ApiService.class) {
                            Log.i( TAG, "post:请求 " + data.api );
                            Log.i( TAG, "post:请求 " + json );
                        }
                    }

                    HttpResult httpResult = httpTask.post();

                    switch ( httpResult.getState() ) {
                        case RESPONSED:
                            String result = httpResult.getBody().toString();
                            Gson gson = GsonFactory.getSingletonGson();
                            CodeMsgData codeMsgData = gson.fromJson( result, CodeMsgData.class );

                            if ( !TextUtils.isEmpty( codeMsgData.data ) ) {
                                codeMsgData.data = AESUtil.desEncrypt( codeMsgData.data );
                            }

                            if ( codeMsgData.code == 200 ) {
                                if ( LOGGABLE.get() ) {
                                    synchronized (ApiService.class) {
                                        Log.i( TAG, "post:响应 " + data.api );
                                        Log.i( TAG, "post:响应 " + json );
                                        Log.i( TAG, "post:响应 " + codeMsgData.code + " " + codeMsgData.msg );
                                        if ( codeMsgData.data != null && codeMsgData.data.length() > 1000 ) {
                                            Log.i( TAG, "post:响应 " );
                                            Logger.json( codeMsgData.data );
                                        } else {
                                            Log.i( TAG, "post:响应 " + codeMsgData.data );
                                        }
                                    }
                                }
                                return Observable.just( codeMsgData );
                            } else {
                                if ( LOGGABLE.get() ) {
                                    synchronized (ApiService.class) {
                                        Log.i( TAG, "post:响应 " + data.api );
                                        Log.i( TAG, "post:响应 " + json );
                                        Log.i( TAG, "post:响应 " + codeMsgData );
                                    }
                                }
                                return Observable.error( new CodeDataMsgException( codeMsgData ) );
                            }
                        case NETWORK_ERROR:
                        case TIMEOUT:
                        case EXCEPTION:
                        case CANCELED:
                        default:
                            IOException error = httpResult.getError();
                            if ( LOGGABLE.get() ) {
                                synchronized (ApiService.class) {
                                    Log.i( TAG, "post:响应 " + data.api );
                                    Log.i( TAG, "post:响应 " + json );
                                    Log.i( TAG, "post:响应 " + error.getMessage() );
                                }
                            }
                            return Observable.error( error );
                    }
                }
            } )
            .subscribeOn( Schedulers.io() );
    }

    /**
     * 下载
     *
     * @param context context
     * @param url     下载地址
     * @param file    下载文件
     */
    public static Observable< Double > downLoad( Context context, String url, File file ) {

        ObjectModel model = new ObjectModel();
        return Observable.create( new ObservableOnSubscribe< Double >() {
                @SuppressLint( "NewApi" )
                @Override
                public void subscribe( @NonNull ObservableEmitter< Double > emitter ) throws Throwable {

                    model.mObject = HTTP.sync( url )
                        .get()
                        .getBody()
                        .setOnProcess( new java.util.function.Consumer< Process >() {
                            @Override
                            public void accept( Process process ) {

                                if ( !emitter.isDisposed() ) {
                                    emitter.onNext( process.getRate() );
                                } else {
                                    if ( model.mObject != null ) {
                                        Ctrl ctrl = (Ctrl) model.mObject;
                                        ctrl.cancel();
                                        boolean delete = file.delete();
                                        if ( LOGGABLE.get() ) {
                                            Log.i( TAG, "下载取消: " + delete );
                                        }
                                    }
                                }

                                if ( LOGGABLE.get() ) {
                                    Log.i( TAG, "下载中: " + process.getDoneBytes() + "/" + process.getTotalBytes() );
                                }
                            }
                        } )
                        .toFile( file )
                        .setOnSuccess( new java.util.function.Consumer< File >() {
                            @Override
                            public void accept( File file ) {

                                if ( !emitter.isDisposed() ) {
                                    emitter.onNext( 1.0 );
                                    emitter.onComplete();
                                }

                                if ( LOGGABLE.get() ) {
                                    synchronized (ApiService.class) {
                                        Log.i( TAG, "下载成功: " + url );
                                        Log.i( TAG, "下载成功: " + file.getAbsolutePath() );
                                    }
                                }
                            }
                        } )
                        .setOnFailure( new java.util.function.Consumer< Failure >() {
                            @SuppressWarnings( "ResultOfMethodCallIgnored" )
                            @Override
                            public void accept( Failure failure ) {

                                IOException exception = failure.getException();

                                if ( !emitter.isDisposed() ) {
                                    emitter.onError( exception );
                                }

                                if ( LOGGABLE.get() ) {
                                    synchronized (ApiService.class) {
                                        Log.i( TAG, "下载失败: " + url );
                                        Log.i( TAG, "下载失败: " + file.getAbsolutePath() );
                                        exception.printStackTrace();
                                    }
                                }

                                failure.getFile().delete();
                            }
                        } )
                        .start();
                }
            } )
            .subscribeOn( Schedulers.io() )
            .observeOn( AndroidSchedulers.mainThread() );
    }

    /**
     * @param context 为heep请求设置
     *
     * @return http请求的客户端信息
     */
    public static String getUserAgent( Context context ) {

        if ( sUserAgent == null ) {
            String userAgent = "";
            String versionName = CommonCode.getVersionName( context );
            String systemModel = android.os.Build.MODEL;
            String systemVersion = android.os.Build.VERSION.RELEASE;
            String deviceBrand = android.os.Build.BRAND;
            userAgent = "_Android/appVersion:" + versionName + "/deviceBrand:" + deviceBrand + "/deviceModel:" + systemModel
                + "/systemVersion:" + systemVersion + "/channel:android";
            sUserAgent = userAgent;
            return userAgent;
        } else {
            return sUserAgent;
        }
    }

    public static class ObjectModel {

        public Object mObject;
    }

    public static class ParameterizedTypeImpl implements ParameterizedType {

        private final Class< ? > mClass;

        public ParameterizedTypeImpl( Class< ? > aClass ) {

            mClass = aClass;
        }

        @androidx.annotation.NonNull
        @Override
        public Type[] getActualTypeArguments() {

            return new Type[]{mClass};
        }

        @androidx.annotation.NonNull
        @Override
        public Type getRawType() {

            return List.class;
        }

        @Override
        public Type getOwnerType() {

            return null;
        }
    }
}